AYA (文) Version 5 說明文書

何謂AYA

AYA的日文即「あや」,漢字是「文」,由 umeiciさん 製作的高機能SHIORI,由於採用近似於C語言的寫法,對於學過程式語言的人而言,特別容易上手。而對於沒有相關背景的人格開發者,AYA的作者也提供了基礎的人格範本*1,只要經過少量的修改,便能成為一個全新的人格。

AYA主要執行下述處理:

  • 對提供的字串進行加工
  • 根據程序的規則生成字串

環境

Windows用。

開發的初期版本有經過98SE、2000、xp的動作確認,不過最終版本並沒有在XP以外的系統上作過動作確認。

使用規定

利用規定(作者原文)

  • 使途を限定せず自由に利用することが出来ます。
    (可以不限定用途的隨意使用。)
  • 配布パッケージに含まれているすべてのファイルを自由に取り扱うことが出来ます。
    一部のファイル、もしくはそのファイルの内容の一部を抜き出して、他の構成に含めてもかまいません。
    (可以自由運用發佈文件包裝中包含的全部文件。)
    (取出一部份檔案,或取出那個檔案中的一部份內容進行二度製作也不介意。)
  • このプログラムを使用した結果、あなたに何らかの損害が発生しても、その責任を作者は負いません。
    (使用此程式後發生的任何問題,作者不予負責。)
  • ソースコードを改変して自由に異版を作成し、配布することが出来ます。
    その際、配布者はその意志に基づいたライセンスを独自に策定し配布物に適用することが出来ます。
    (改變原始碼並自行製成不同版本者,可自行發佈。)
    (在此情況下,發佈者能基於自己的意志自行製定適用於此發佈物的認證。) 

匯出的函式

文有以下被公開的函式。
文を利用するプログラムは、文をLoadLibraryした後にこれらの関数を実行して所望の処理を行ないます。

extern "C" __declspec(dllexport) BOOL __cdecl load(HGLOBAL h, long len)

文初期化指示。
文をLoadLibraryして使用を開始する直前に、この関数を一度だけ必ず実行してください。
hには「文がカレントとして認識するディレクトリ絶対パス」を、lenはhの長さを渡してください。 hの領域開放は文側で行ないますので、呼び出し側では使い放しでかまいません。

extern "C" __declspec(dllexport) BOOL __cdecl unload()

文終了指示。
文をFreeLibraryする直前に一度だけ実行してください。

extern "C" __declspec(dllexport) HGLOBAL __cdecl request(HGLOBAL h, long *len)

文に処理を指示し、結果を得ます。
hには処理対象の文字列を、*lenにhの長さを渡してください。 渡したhの領域開放は文側で行ないますので、呼び出し側では使い放しでかまいません。 処理結果は戻り値で得られます。処理結果の長さは*lenに格納されています(つまりこの値は書き換えられます)。 戻り値を取得した後、領域を*lenで示されるサイズで開放(GlobalFree)してください。

此外,這部份和桌面應用軟體「伺か」使用的擬似AI用DLL「SHIORI」的介面規格完全是相同的。

基礎設定

文 ver.5的預設DLL檔名為「aya5.dll」。
主檔名「aya5」可以自由地改為別的名字。

為了讓文正常作動,被稱為「基礎設定檔案」的檔案是必要的。
基礎設定檔案的檔案名為「主檔名.txt」。即預設為「aya5.txt」。如果你把DLL的檔案名改為「hoge.dll」的話,基礎設定檔案則為「hoge.txt」。

基礎設定檔案為TEXT文件、實行時以OS預設的文字編碼來解讀。
若要考慮國際化方面的情況的話,為了迴避關於多位元組文字代碼的問題、基礎設定檔案最好是設成ASCII編碼。

以下述為例:

// dics
dic, basis.dic
dic, control./*doc*/ayc

// option parameters
charset, UTF-8
charset.dic, Shift_JIS
charset.output, UTF-8
charset.file, Shift_JIS
charset.save, UTF-8
charset.extension, Shift_JIS

msglang, english
log, executelog.txt
iolog, off
fncdepth, 16

設定時用逗號劃分命令和參數。 空行(只換行的行)、『//』後面單行文字、『/*』到『*/』包圍的範圍皆不會被讀取。

命令和其意義為如下所示:

  • charset, name
    標準文字編碼的設定。
    從以下的預約值任意選擇一個。 沒有指定的時候則預設作為Shift_JIS處理。

    • Shift_JIS / ShiftJIS / SJIS
      日文Shift_JIS編碼。
    • UTF-8
      萬國碼。
    • BIG5
      繁體中文。
    • default
      OS預設的文字編碼。
  • charset.dic, name
    辭書的文字編碼。

  • charset.output, name
    request時使用的文字編碼。

  • charset.file, name
    讀寫外部文件時使用的文字編碼。

  • charset.save, name
    全局變數保存文件讀寫時使用的文字編碼。

  • charset.save.old, name
    讀取沒有指定文字編碼的舊保存文件時使用的文字編碼。

  • charset.extension, name
    使用外部DLL(SAORI等)時傳遞的文字編碼。

  • dic, filename
    讀取辭書檔 filename。
    辭書檔為交付 AYA腳本 作為程式源始碼的文件,文會根據此程式的內容進行作動。
    辭書檔指定多少,就能讀取多少。

    filename為load指定的路徑位置的相對路徑。
    標準的辭書檔是用 charset 指定的文字代碼記述的平面文件。至於其他的,也可以用一定的法則來讀取一些經過暗號化的亂碼文件。

  • msglang, language
    選擇 log 記錄的錯誤訊息所使用的語言。
    從以下的預約值任意選擇一個。 沒有指定的時候則預設作為 japanese 處理。

    • japanese
      日本語。
    • english
      英語。
  • log, logfilename
    實行 log 檔案 logfilename。
    能用 charset 指定的文字編碼寫入。

    logfilename 為 load 指定的路徑位置的相對路徑。

  • iolog, [on|off]
    設定是否記錄 load、unload、request 實行時的輸入輸出數據、字串及處理時間。
    on為記錄。預設為on,不需要時請設為off(節省系統資源)。

  • fncdepth, depth
    指定一數值限制函式呼叫的深度上限。
    預設値為32。最低値為2、比2還小或錯誤參數時也視為2。

AYA腳本參照

AYA腳本大致上的語法抄襲自C語言。 若曾學過C語言,在學習類似於C語言的AYA時便易於理解。

函式

基礎

以下為request實行時能傳回字串"Hello World"的代碼。

   request
   {
       "Hello World"
   }

文讀取此模組 HGLOBAL request(HGLOBAL h, long *len) ,實行此腳本後,便傳回"Hello World"。

load與unload是同樣的。
但因為load與unload不傳回值、所以即使寫入輸出字串也沒有意義。load為代入變數的初期值進行初期化處理、相反的unload將處理後的code寫入。

只要寫必要的函式就好。不必要的則可省略。
例如上例中的load和unload由於沒有寫入、因此也不會發生錯誤。僅僅是什麼也沒做。
舉個極端的例子、即使完全沒有辭書檔也不會發生錯誤。這種場合下load和unload什麼也沒有做、 request則傳回空的字串。

load和request有一個參數。可以用變數取出此值。
變數的名字為_argc和_argv、跟C語言的main函式的介面相類似。

_argc

參數的數量。load和request的參數是一個字串,所以值為1。unload沒有參數、因此值為0。

_argv

參數的實體存在此處。此為具有_argc個元素的陣列。各元素中的存取位址為運算子[i]。
序數i從0開始。例如_argc是2的情況時可使用_argv[0], _argv[1],參數會儲存在此。

總結基礎編的內容,load 將 "Hello" 收納進變數str, request 將作為參數交出的字串與 str 結合後傳回的程式碼顯示出來。

請以上述的說明為基礎讀讀看。

   load
   {
       str = "World"
   }
   request
   {
       str + "Hello" + _argv[0] + "!"
   }

將處理對象字串設為"World"交予 request 實行,可以得到 "Hello World!" 的結果。

語法

有以下之例:

  1. 空行(只換行的行)、"//"之後、及"/*"和"*/"包圍的範圍不會被讀取。
  2. 1行的末端以斜線("/")作結時、下一行會與此行結合。
  3. 行頭、以及單語間可自由放入空白文字。空白文字為空白及標記文字。
  4. 複數的敘述寫在1行內的場合時、可以用分號(";")區別開來。

也就是說先前舉例的Hello World代碼可以下列方式寫在一行之中。

   request{"Hello World"}

那麼像以下這樣的寫法可以嗎? 當然。運算上不會有問題。
(但是這種寫法不是很好看!)

   req/
   uest          {
   "/
   Hello World"  }

斜線"/"的下一行(被重新結合的行)先頭的空白文字會被判定為無效文字而消除。
同樣的以下字串的結合結果為 "ABCDEFG" 、不會是 "ABCD  EFG" 。

   "ABCD
       EFG"

第4項説明。
以下request為求1+2的答案、並傳回句子。

   request
   {
       answer = 1 + 2
       "答案是" + answer + "的樣子。"
   }

用分號的話可以寫成以下這樣。

   request
   {
       answer = 1 + 2; "答案是" + answer + "的樣子。"
   }

分號過多時不會造成問題。它們會被無視,不影響運算的結果。
相對的,如果你已經習慣C語言式的寫法,也可以在每行的後面都加上分號。

自定義函式的定義與實行

除了 load、unload、request 以外也可以製作你自行取名的函式。
寫好函式後,只要呼叫此函式即可使用。

   request
   {
       hello
   }
   hello
   {
       "Hello World"
   }

上面表示的是最單純的例子。request的結果會傳回"Hello World"。

為了完成一些工作,大部份的函式會需要一個或者是更多的參數(parameter),而參數是讓你可以傳遞一些資料到函式中。以下的例子可以得到與上述例子同樣的結果。

   request
   {
       combine("Hello", "World")
   }
   combine
   {
       _argv[0] + " " + _argv[1]
   }

函式名後面加入( ),並在內部以逗號分列數值,這樣一來這些值會作為參數交給該函式。即作為變數_argv和_argc的值,可在此函式内被提取。
在此舉的例子當中、combine函式内的_argc為2、_argv[0]為"Hello"、_argv[1]為"World"。

函式名可以自由取名,但不能與下列的規則相扺觸。

  • 用數字0~9作為第一個字。
  • 以下標線("_")為第一個字。
  • 包含以下文字。
         空白 ! " # $ % & ( ) * + , - / : ; < = > ? @ [ ] ` { | } ~
  • 與預約語完全一致。

函式重複呼叫是可能的。
舉最常見的層乘計算式作為例子。

   request
   {
       factorial(5)
   }
   factorial
   {
       if !_argv[0]
           1
       else
           factorial(_argv[0] - 1)*_argv[0]
   }

request傳回120。

字串選擇

   request
   {
       "Hello World"
       "你好世界"
       "Hallo Welt"
   }

像這樣的例子的話、這三個句子會被視為同等的「輸出候補」、輸出三者之中的任意一個。

一共有5種選擇方法,可以任意選擇其中一種。
然而, void 與 array 是為特殊用途而用的。

不指定

預設為隨機選擇。

nonoverlap

在所有的候補都被選過之前,不會選擇重複的選項。

         request : nonoverlap
         {
             "Hello World"
             "你好世界"
             "Hallo Welt"
         }

像上列這樣在函式後面附加": nonoverlap"。

sequential

上至下按順予輸出。輸出到最後時,會返回先頭。

         request : sequential
         {
             "Hello World"
             "你好世界"
             "Hallo Welt"
         }

像上列這樣在函式後面附加": sequential"。

void

什麼都不輸出。
以下的場合、request 3個候補都不會輸出。

         request : void
         {
             "Hello World"
             "你好世界"
             "Hallo Welt"
         }

「什麼都不做」與「什麼都不輸出」之間的差別請注意一下。
函式内部的函式與算式仍然是會被處理的。

         increment_i : void
         {
             i++
             i
         }

上列函式中increment_i的i會進行1的加算。在沒有void的場合下、此函式會傳回加算的結果、在有指定void的場合下,不會傳回任何的值,僅只是進行了加算的動作。

array

輸出候補會全部集成一個泛用陣列作為函式的返値。

         request : array
         {
           "This is a pen."
           ("A","B","C")
           3.14
         }

輸出結果等同於泛用陣列 ("This is a pen.", "A", "B", "C", 3.14) 。

nonoverlap和sequential即使在具有輸出確定子的情況下,也能取得所有的組合進行正常的運作。
舉sequential的例子來說:

   request : sequential
   {
       "1"
       "2"
       "3"
       --
       "A"
       "B"
   }

request會照以下順序輸出。

   "1A" "2A" "3A" "1B" "2B" "3B" "1A" "2A" …

有時函式會使輸出候補數量產生變動。
例如以下的函式會因為變數i的值而使候補數由2個變成4個。

   request : sequential
   {
       if i {
           "1"
           "2"
       }
       "3"
       "4"
   }

當候補數產生變化後、nonoverlap和sequential的巡迴順序會初期化,重新回到一開始的狀態。唯有此時才有可能輸出和上回相同的值。

子階層{}

   request
   {
       {
           "Hello World"
           "你好世界"
       }
       
       "Hallo Welt"
   }

{ } 可以階層式的重複書寫。其中變化與最高層的{ }相同,會在包含其中的候補挑選一個輸出。
但只能以隨機的選擇方法,無法像最上層那樣指定nonoverlap或sequential。

從上述來看,{ }的有無似乎是一樣的,但實際上不是這樣。
在沒有{ }的場合、輸出的機率大約是平等的1/3。然而在上述的例子看來、首先會從"Hello World"與"你好世界"之中挑選一個出來、然後挑選出來的再與"Hallo Welt"進行二選一。所以說,三者的出現率為"Hello World"與"你好世界"為1/4,"Hallo Welt"為1/2這樣。

輸出確定子

   request
   {
       "Hello"
       "Perfect"
       "Peaceful"
       --
       " Wor"
       --
       "ld"
       "th"
   }

『--』作為輸出確定子,會將選擇候補的範圍切開來分成各自的小組。然後,將各小組選出來的結果互相結合。
如果實行上述的request的話,會產生下列任一種結果。

   "Hello World"
   "Perfect World"
   "Peaceful World"
   "Hello Worth"
   "Perfect Worth"
   "Peaceful Worth"

與nonoverlap、sequential組合使用的場合下,(グループ單位ではなく)函式會根據取得的所有組合進行相對的動作。

輸出確定子在任何地方都可使用,即使在{ }很深的情況下。

文也能處理同時有字串與數値的情況。
輸出確定子會在結合時全部轉換為字串與字串的結合。

値與變數

即値

文能處理的值為整數、實數、字串3種類。

  • 整數
    帶符號32bit整數。

    一般為10進位數値。
    在前頭附加"0b"的話、可記述2進位數値。
    在前頭附加"0x"的話、可記述16進位數値。

    以下的函式int10會傳回整數10。因為3種記述方式以10進位來看都一樣是10。
         int10
         {
             10
             0b1010
             0xa
         }
  • 實數
    帶符號64bit浮動小數點數。數值在小數点以下的場合、或是精確度落差非常巨大的場合時可以使用這個。
    與整數的差別在於小數點的有無。文在數值上有小數點的時候,會作為實數處理。
  • 字串
    雙引號(")包圍的値為字串。
    字串中不可含有雙引號。
  • 字串(無展開)
    單引號(')包圍的值,不進行展開動作的單純字串。
    文能在字串中插入變數或函式的這個功能,是在有展開被雙引號包圍的字串才有。
    字串中不可含有單引號。

變數

變數為保存值的領域。
變數可以保存:

  • 整數、實數、字串
  • 可以保存上述三者的泛用陣列

上述的這幾種。

名字在不觸犯以下禁止條款的前提下可自由設置。

  • 以數字0~9為始。
  • 包含以下文字。
         空白 ! " # $ % & ( ) * + , - / : ; < = > ? @ [ ] ` { | } ~
  • 與預約語完全一致。
  • 與函式名完全一致。

値的保存(指派)由指派運算子 = 來執行。要輸出内容的話、與函式一樣寫下變數的名字即可。

   request
   {
       str = "你好"
       
       str
   }

上面為最單純的例子、變數str存取字串、並照著那樣輸出。

若是未指派值的變數,則會輸出空的字串。
注意並不是「什麼也不輸出」。

   request
   {
       "Hello World"
       i
   }

i 並不存在,因此為空的字串,結果上面的例子與下面的寫法等價,因此有1/2的機率會輸出"Hello World"或空字串。

   request
   {
       "Hello World"
       ""
   }

變數的範圍與壽命

變數有兩種不同的scope(有效範圍)。

  • 全域變數(global variable)
    所有函式皆可共通使用的變數。壽命為永遠。
  • 區域變數(local variable)
    現在的{ }内、以及其下更深的子階層可以使用的變數。壽命為當此{ }結束的時候。

兩者的區別為變數的名字。變數名前頭有下標線("_")的變數為區域變數。
以「只在必要範圍內有効的變數」的觀念善用區域變數的話、可以有效提升程式的品質。

   request
   {
       _i = "3*2等於"
       
       _j = multi(3)
       
       _i +_j + "的樣子"
   }
   multi
   {
       _i = _argv[0]
       _i * 2
   }

request與multi雖然使用了同樣名字的變數 _i,但兩者被視為完全無關的值來運算,也不會互相干擾。

你或許也察覺到函式的參數所使用的變數_argc和_argv同樣也是區域變數。
這也是因為其他的函式也都需要存取一些不同的值,所以才將其作為區域變數。

區域變數並非是「在現在的函式内可以使用的變數」,而是「現在的{ }内、甚至更深的階層都可使用的變數」,這點請注意一下。

   request
   {
       {
           _str = "Hello World"
       }
       
       _str
   }

這個程式無法照著我們希望的那樣動作。
_str在{ }内是有效的、但是到了輸出取值的時候卻消失了。
結果,這個函式request輸出的是空字串。
要正確產生結果,則應該預先宣告變數。如下所示:

   request
   {
       _str = ""
       
       {
           _str = "Hello World"
       }
       
       _str
   }

全域變數與區域變數的差異就在這裡。就是壽命。
照上述所說區域變數的規則、變數在現在使用的{ }之外便會消失。
相對的全域變數在哪裡都可使用、此外unload後此值會被自動儲存在檔案裡面,等到load後又可再度復元。意即全域變數的內容(在沒有特別的操作或意圖使其消去的前提下)是可以永久保存的。

運算

基本

與C語言同様寫法的四則運算、比較運算、指派、及其他可能的運算。
演算順序為當運算子重複堆疊時基於演算優先度來決定的。還有、括弧( )包圍的部分為最優先演算順序。

運算子的種類與演算優先度如下。

運算子意義優先度
( ) [ ]括弧
!否定*
++ --遞增*/遞減*
* / %乘除算、除餘
+ -加減算
&反饋*
== != >= <= > < _in_ !_in_比較
&&論理積
(註)論理和
= :=指派
+= -= *= /= %= +:= -:= *:= /:= %:= ,=演算並指派
,泛用陣列要素的列舉
(註)論理和為 ||
附有冒號(":")的指派運算子乃因舊有版本的相容性而保留的,功能上與沒有冒號的指派運算子完全相同。
*為單項運算子。

括弧( )相關內容會在次項中詳細說明。
逗號運算子(",")、方括弧[ ]會在陣列的項目中詳細說明。
反饋運算子&會在別項中詳細說明。

_in_與!_in_為字串中包含校驗功能的運算子。

   foo
   {
       "or" _in_ "World"
   }

_in_ 若左邊字串在右邊字串中有相符字串的話傳回1、如果沒有的話傳回0。!_in_為相反。
上述函式foo傳回1。

比較運算子的結果是真時傳回整數1、偽時傳回0。
這些運算子同樣也適用於字串。値的大小則根據辭書順序的比較來決定。

邏輯真偽的判斷。

偽 整數0、實數0.0、空字串、空泛用陣列
真 上述以外的全部值

沒有指派的運算會直接輸出結果。

   foo
   {
       (3+2)*4
   }

此函式foo最後會輸出20。

同樣優先度的運算子連續出現的場合下,通常由左開始結合。

   1+2-3

例如上式為
(1) 1+2
(2) 3-3
的順序進行計算。

   i = j = 10

至於這種例子又怎麼說呢?因為C語言的指派運算子為由右至左的演算,所以 i 與 j 都被指派了10的值。
然而文在結合時常由左邊開始。即
(1) i = j
(2) j = 10
這樣的順序計算下去。結果、i 無法變成10的值。

   i = (j = 10)

改成這樣寫的話 i 就可以得到10的值了。

演算對象項目的型別不一致的場合下、最終結果的型別如下列所示。

  • 整數與實數的演算
    結果為實數。
  • 整數/實數與字串的加算
    數値轉換成字串後、再與字串結合。
  • 整數/實數與字串的演算(加算以外)
    無法演算但也不會產生錯誤。結果為空字串。

即使在一個演算式内混合多種的型別也沒關係。
必要的話、會根據上述法則產生型變。

   "10+2等於" + (10+2) + "的樣子。"

一開始是10+2的整數計算,得到的值為12。接下來為所有字串的加算,12轉換成字串,作為字串組合在一起。

有括弧( )時的演算順序

括弧( )包圍的部分為演算順序中最優先展開的。
( ) 可以任意重複指定、愈深的優先度愈高。

平白的說就是「從包圍最深的地方先開始計算」。因為這是理所當然的規則,所以也不用特別在意。不過按照數式的寫法也是可以弄得非常複雜的。
以以下例子來看。

   answer = (_i = 10) + (2*(_i + 10))

對於不理解文的演算法則的人來說,很難保證answer會是怎樣的值吧。
answer其實是字串的"10"。絕對不是整數50。

最初的計算在哪?括弧最深的地方、也就是 _i + 10 。那麼變數 _i 還不存在,因而變成空字串。再來、_i + 10 為字串與整數的加算。10也變成字串、也就是"10"。但是接下來為與整數的乘算、結果又變成了空字串。
說到這裡應該明白為何是字串的"10"原因了吧。

那麼、這邊的意圖首先是讓_i = 10的指派成為最初的演算。
像這種時候、文的方法即為追加更多的括弧來控制演算的順序。

   answer = (((_i = 10))) + (2*(_i + 10))

這樣一來指派的優先度就大為提升。這回應該便能得到正確的結果了。

然而,如果你以為將括弧加到第2層即可的話,那你又錯了。括弧2段的話雖然與 _i + 10同樣深度,但是 = 與 + 相比優先度是 + 的那邊較高。

反饋運算子&

反饋運算子&是用法完全不同於別的運算子的獨特運算子。

   request
   {
       _i = 1
       foo(&_i)
   }
   foo
   {
       _argv[0] = 100
   }

呼叫函式時將變數作為參數的時候、可以在變數的前面加上&。前頭附有&的變數可以與原先叫出函式相對應的_argv的元素產生關聯。也就是說、當_argv的值改變的時候、其對應的原呼叫變數的值也會改變。
就上面的例子、foo實行後 _i 的値也會變成100。

反饋運算子可以自由的在不同地方多次使用。

   request
   {
       foo(1, 2, &_value, "Hello", &_value2)
       
       _value + _value2
   }
   foo
   {
       _argv[2] = _argv[0] + _argv[1]
       _argv[4] = _argv[3] + " World"
   }

函式request最終傳回 "3Hello World" 。

當然的、反饋運算子只能使用在變數上面。

陣列

運算子[ ]為用於存取陣列元素的運算子。

陣列分為在字串區分元素來擬似陣列型式的「簡易陣列」,以及以逗號區分、詳細列舉陣列元素的「泛用陣列」這兩種類。

簡易陣列

將字串中含有的逗號視為分隔符號(劃分的符號),並像陣列般進行處理。
簡單的說,簡易陣列其實就是字串,只是用了類似處理陣列的方式進行演算。

   request
   {
       _a = "this,is,a,pen"
       _a[1]
   }

request最後輸出"is"。

[ ]運算子處理的對象並不一定是變數,即値與函式的返値也是可以的。
上述例子也可寫成如下例這樣。

   request
   {
       "this,is,a,pen"[1]
   }

在[ ]運算子中的第二項參數可以自行指定分隔符號(delimiter),用逗號以外的文字作為劃分陣列元素的根據。

   request
   {
       "This,is,a,island."[2,"is"]
   }

上述即以"is"來進行區分,字串最終會被分解成這樣。

   [0] "Th"
   [1] ","
   [2] ",a,"
   [3] "land."

request因而傳回",a,"。

如果善用分隔符號的話,要做出類似多次元陣列格式的值也是有可能的。

   request
   {
       _ar = "taro|male,ayame|female,hotaru|female"
   	
       _ar[2][1,"|"]
   }

_ar[2]即為"hotaru|female"。接下來再將"|"作為劃分文字將"hotaru|female"中的[1]取出的話、結果即為"female"。像這樣每個階層都使用獨特的分隔符號的話,便可以輕鬆取得任意位置的值。

當你指定範圍以外的序數時,會跟取出不存在的變數的情況一樣,傳回的值為空字串。

這邊開始為變數才能使用的功能。

可以指派陣列元素。

   request
   {
       _a = "this,is,a,pen"
       _a[3] = "eraser"
       _a
   }

"pen"會被替換成"eraser"。request的實行結果為"this,is,a,eraser"。

在有指定分隔符號的情況下也能正常運作。

   request
   {
       _s = "This,is,a,island."
       _s[2,"is"] = ",beautiful,"
       _s
   }

request會輸出"This,is,beautiful,island."。

在與多次元陣列[ ]運算子連結使用的時候,則無法指派( = )運算子。

   request
   {
       _ar = "taro|male,ayame|female,hotaru|female"
   	
       _ar[1][1,"|"] = "male"
   }

上述式子想把ayame的性別改為male,但卻會發生錯誤。只有在一次元的情況下才能指派。

指派的位置即使超過現有的元素數量也沒關係。分隔符號會自動追加,擴張元素數量。

   request
   {
       _m = "fuji/asama/tanigawa"
       _m[5,"/"] = "daisen"
       _m
   }

request會輸出"fuji/asama/tanigawa///daisen"。

如果使用叫做SETDELIM的函式的話,便能將「預設的分隔符號」從逗號改為其他的字串。 上述例子如果用SETDELIM來寫的話,會變成下面這樣。

   request
   {
       _m = "fuji/asama/tanigawa"
       SETDELIM(_m, "/")
       _m[5] = "daisen"
       _m
   }

運行SETDELIM之後,只要寫_m[5]即可。

在與多次元陣列[ ]運算子連結使用的情況下、SETDELIM只對最初的(一次元的)[ ]有効。

泛用陣列

泛用陣列是可以保存各種不同型別的值的陣列構造。
一般的存取速度要比簡易陣列快得多。

初期化

   i = (100,"test",-1.5)

用逗號列舉陣列元素並記述。
在指派的時候請像上面一樣將元素的集合用( )圍起來。因為逗號的演算優先度低於指派運算子( = ),如果沒有這麼做的話會被

   (i = 100),"test",-1.5

這樣子解釋。

要使陣列呈現無元素狀態的初期化,請使用IARRAY這個函式。IARRAY為會傳回「空的泛用陣列」的函式。

   i = IARRAY

初期化時如果只有指派一個陣列元素時得另外做點功夫。假如只有指派i = 100的話,因為不是陣列的關係,會被視為單純的指派100的值。
請照下述方式記述。

   i = (IARRAY,100)

陣列元素的追加

   i = (i,"add")

這樣寫的話會在陣列i的後端追加"add"這個元素。

也可以追加陣列。

   i = (i,("add",123,0.0))

就像a = a + 1 同等 a += 1這樣的省略法,上述例子也可以寫成像下面這樣。

   i ,= ("add",123,0.0)

也可以在陣列的前端插入元素。

   i = ("first",i)

也可以在陣列的中間插入元素。

   i = (100,200,300,400,500,600)
   i[2] ,= "insertion"

i即為(100,200,300,"insertion",400,500,600)。
要注意的是插入的值是追加在i[2]的後面,所以當你要取這個值的時候不是取i[2]而是取i[3]。想插入i[2]的位置的話請

   i[2] = ("insertion",i[2])

這樣子寫。

陣列元素的削除

將要削除的元素指派給IARRAY。

   i = (100,200,300,400,500,600)
   i[2] = IARRAY

300於是被削除、i變成(100,200,400,500,600)。

値的更新

可以單純的指派元素。

   i = (100,200,300,400,500,600)
   i[2] = 700

i變成(100,200,700,400,500,600)。

指派的位置即使超過現有的元素數量也沒關係。陣列會自動擴張元素數量。

値的取出

與通常的變數一様,被指定的元素若存在便輸出此陣列元素。

當你指定範圍以外的序數時,會跟取出不存在的變數的情況一樣,傳回的值為空字串。

   i = (100,200,300,400,500,600)
   i[4]

最後輸出500。

[ ]運算子處理的對象並不一定是變數,即値與函式的返値也是可以的。

   (100,200,300,400,500,600)[4]

最後輸出500。

泛用陣列也可直接作為函式並輸出。

   request
   {
       river[2]
   }
   
   river
   {
       "tenryu","bandou-tarou","ishikari","shimanto"
   }

request會輸出"ishikari"。

無法多次元化

泛用陣列無法組合成多次元陣列。

   (100,200,(300,400),500,600)

用這樣的寫法時,括弧內包的部份並不會被視為第二次元的陣列。
結果像以下一樣單純的結合。

   (100,200,300,400,500,600)

演算

元素單位的演算一般來說是可以的。

比較獨特的是,泛用陣列的單項値在演算的時候,是
「全元素的單項値都進行演算」。

       pref = ("gunnma","ohsaka","hokkaido")
       pref += "-ken"
       answer = (2*(1,2,3))[1]

pref會變成 "gunnma-ken","ohsaka-ken","hokkaido-ken" 。

answer的結果是4。
2*(1,2,3)的計算結果為(2,4,6)。

函式的參數

在文的體系中,函式的參數為泛用陣列。_argv的內容即為函式呼叫時所代入的參數。

也就是說

   func(1, 2, "test")

與這樣的函式呼叫、

   _i = (1,2,"test")
   func(_i)

都可以寫入。這是相當重要的記述。
請注意,這個script在運行的時候,參數的數量絕對不是1個。而是3個!
此時func內_argv[0] = 1,_argv[1] = 2,_argv[2] = "test"。

善用這種構造的話,可以從其他的函式中簡單的取得可變長的參數。

   request
   {
       total(1,2,3,4,5,6)
   }
   
   total
   {
       calc_total(_argv)
   }
   
   calc_total
   {
       _answer = 0;
       foreach _argv; _val { _answer += _val }
       _answer
   }

total本身完全沒有做什麼事,只是單純的將所有的參數傳給calc_total。
這例子是單純的傳遞,當然也是可以在此過程中經過必要的加工後再傳遞。

參數的指定方法較為複雜的情況下需要注意幾點。

   _i = (1,2,"test")
   func("sky", _i, "sun")

上述的呼叫與下例同等。使用泛用陣列時請記得它無法多次元化。

   func("sky", 1, 2, "test", "sun")

分隔符號/取得數指定

   _i = (2,"is")
   "This is a island."[_i]

簡易陣列在指定分隔符號的部分也是泛用陣列。所以也能像上述這樣子寫。與下述寫法為等價。

   "This is a island."[2,"is"]

範圍指定

簡易陣列/泛用陣列都可指定序數的範圍,也可以取得或代入。

範圍可用泛用陣列來指定。例如 i[a,b] 可表示為「i的元素a~b」。

   name = ("さくら","せりこ","奈留","まゆら","毒子","美耳")
   i = name[1,3]
   name[3,4] = "奎子"
   j = name
   name[0,2] = IARRAY
   k = name

i變成 ("せりこ","奈留","まゆら") 。
j變成 ("さくら","せりこ","奈留","奎子","美耳") 。
k變成 ("奎子","美耳") 。

範圍外則自動無視。

   n = (1,2,3,4)
   n[-2,1] *= 5

n為 (5,10,3,4) 。

對象是簡易陣列的時候也是一樣。

   name = "さくら,せりこ,奈留,まゆら,毒子,美耳"
   i = name[1,3]
   name[3,4] = "奎子"
   j = name

i為 "せりこ,奈留,まゆら" 、jは "さくら,せりこ,奈留,奎子,美耳" 。

指定範圍後再接著指定分隔符號也是可能的。

   animal = "熊!兔!貓!狗!鱷"
   i = animal[0,2,"!"]
   animal[2,4,"!"] = "豬"
   j = animal

i變成 "熊!兔!貓" ,j變成 "熊!兔!豬" 。

泛用陣列的平行輸出

所有的式/値的前面都可寫上「parallel」。
具有parallel的泛用陣列在作為輸出候補値的時候,能將陣列中所有的元素視為輸出候補値。

   foo0
   {
     ("A","B","C")
     "地球"
   }
   
   foo1
   {
     parallel ("A","B","C")
     "地球"
   }

foo0的輸出會是("A", "B", "C") 或 "地球"。
foo1的輸出會是"A"、"B"、"A"、"地球" 這四種。

把parallel用在泛用陣列以外的值的時候,有寫跟沒寫一樣。例如下面的2種寫法是等價的。

   parallel STRLEN("earth")
   STRLEN("earth")

在使用字串選擇模式array和parallel的時候,可以與函式的輸出候補和泛用陣列互相配合使用。
可以有各式各樣的應用方式。例如以下非常簡潔的函式cyclic,會把泛用陣列的所有元素按照前後順序取出來。

   request
   {
   	_i = ("甲","乙","丙")
   	cyclic(_i)
   }
   
   cyclic : sequential
   {
     parallel _argv
   }

字串內埋入元素的展開

可以在字串的中埋入變數或函式,並把實行的結果插入其位置上。

附帶範圍的展開

將埋入的元素以%( )包圍。

   request
   {
       _i = "pen"
       "This is a %(_i)."
   }

request實行後,會輸出"This is a pen."。

%( )會進行類似eval(將字串解釋為Script code並實行)的動作。可以是單一的函式或變數,甚至是算式也沒問題。

   request
   {
       "1+2+3等於%(1+2+3)。"
   }

request會輸出"1+2+3等於6。"。

請注意,文的字串中無法包含雙引號,因此不能插入含有字串的算式。以下例子會產生錯誤。

   request
   {
       "This is a %(_i = "pen")."
   }

像這種時候,要將算式區隔出來。

   request
   {
       "This is a " + (_i = "pen") + "."
   }

有括弧( )的演算順序控制與附帶範圍的展開是同様的程序。
請看以下的例子。

   request
   {
       "行星「%(_i = planet)」離earth很遠。這個行星的顏色是%(color(_i))。"
   }
   
   planet
   {
       "mars"
       "saturn"
       "pluto"
   }
   
   color
   {
       case _argv[0] {
       when "mars";   "red"
       when "saturn"; "yerrow"
       when "pluto";  "blue"
       others;        "unknown"
       }
   }

由於最深的( )為color(_i)的參數,所以會在執行_i = planet之前就先呼叫函式color。
在這種情況時,請增加額外的括弧來調整演算順序。

   "行星「%((_i = planet))」離earth很遠。這個行星的顏色是%(color(_i))。"

這樣一來即可得到沒有矛盾的字串。

名稱最長一致展開

沒有授與( )時、單純埋入%時所展開的機能。

   request
   {
      o      = "pen"
      obj    = "eraser"
      object = "world"
      "This is a %object."
   }
   
   obje
   {
       "television"
   }

其展開的對象為%後面的字串中相符並具有最長一致性的變數/函式。在上面的例子中,一致性最高的變數為object,因而採用此變數。結果會變 成"This is a world.",而不是"This is a televisionct."或"This is a eraserect."。

由於變數時時刻刻都在作成與消失,因此展開的對象也會根據狀況而有所變化。
這是附帶範圍的展開時所沒有的特性。

   request
   {
      val   = "red"
      trans
      --
      value = "blue"
      trans
   }
   
   trans
   {
       "%value"
   }

trans被實行了兩次,然而第一次與第二次的"%value"的動作是不一樣的。此意味著最初的運行被視為變數val+"ue",第二次則被解釋為變數value。
request會輸出"redueblue"。

使用%[ ]語法的話可以呼叫過去的展開結果。

   request
   {
       "「%planet」很遠。「%city」也很遠。不過%[0]比%[1]更遠。"
   }
   
   planet
   {
       "mars"
       "saturn"
       "pluto"
   }
   
   city
   {
       "newyork"
       "moscow"
       "madrid"
   }

%[i] 為從 0 開始的第 i 次展開結果。
也就是說在上述的例子中,%[0] 會顯示 %planet 的展開結果,%[1] 會顯示 %city 的展開結果。

%[ ]在附帶範圍的展開中無法使用。想要再利用附帶範圍展開的過去結果的話,請使用變數。

由於名稱最長一致展開在運行過程中會對展開的對象進行檢索,所以和附帶範圍展開相比,動作速度上會慢上很多。
因此在沒有必要的時候,請使用附帶範圍展開%( )。

流程控制

if的分歧

式的判定結果為真時則繼續處理{ }内的式子。

   request
   {
       if !i {
           "i為0。"
       }
   }

可以在後面加入elseif。會在if的判定為偽的時候進行處理。
elseif可連續使用。
另外,if~elseif的最終端可加入else。會在前端if及elseif的判定皆為偽的時候進行處理。

   request
   {
       if !i {
           "i為0。"
       }
       elseif i == 5 {
           "i為5。"
       }
       elseif "A" _in_ TOUPPER(i) {
           "i為字串,並且含有a或A。"
       }
       else {
           "i不是0或5或含有a的字串。"
       }
   }

if、elseif、else在處理script時若只有一行的話、{ }可省略。因此上述程式碼可寫成如下所示。

   request
   {
       if !i
           "i為0。"
       elseif i == 5
           "i為5。"
       elseif "A" _in_ TOUPPER(i)
           "i為字串,並且含有a或A。"
       else
           "i不是0或5或含有a的字串。"
   }

但是在if重疊的時候{ }不可省略。以下式子在C語言中是正確的,在文中是錯誤的。

   if i == 0
       if j == 0
           "i和j都是0。"

以下式子的{ }是必要的。

   if i == 0 {
       if j == 0
           "i和j都是0。"
   }

和C語言一様,if的判定式可以用括弧( )將全部式包圍住。
其運作和不包圍的時候沒有兩樣。
可以使用if、elseif、case、while、for、switch的判定式。

case的分歧

case可實現條件分歧的構造。

   request
   {
       case i {
           when 0 {
               "i為0。"
           }
           when "A"
               "i為字串A。"
           others {
               "i不是0也不是A。"
           }
       }
   }

case會實行與判定式結果一致的値所對應的when片段。
others在代入的值與所有條件敘述的值都不一致的時候才會執行。others可以被省略。

when可以用逗號列舉兩個值以上的條件敘述。另外也可以用減號『-』來指定條件的範圍。

   request
   {
       case name+(i+1) {
           when "Pentium3","Pentium4"
               "Pen!!!在1999年發售,而Pen4則在2000年發售。"
           when "Pentium5"-"PentiumX" {
               "還沒有。"
           }
           others
               "我不知道。"
       }
   }

when所記述的條件式必須是即值,不能含有變數或函式、運算子。

when、others在處理script時若只有一行的話,{ }也可像if一樣省略。

switch的分歧

當你想要自行選取{ }内的輸出候補的話,使用switch的話即可指定輸出候補的位置。

   request
   {
       switch id {
           "id為0。"
           "id為1。"
           {
               "id為2。"
               "id為two。"
           }
           "id為3。"
       }
   }

會依照變數id的値來指定輸出的字串。指定的值從0開始。
id為2的時候,會輸出"id為2。"或"id為two。"。在此{ }所包含部分的選擇為隨機。

當switch的參考值所對應的候選不在{ }内時輸出空字串。例如說、在上述例子中若id等於100,便會輸出空的字串。

當switch内含有輸出確定子( -- )的時候,會選擇各小組在此值所配對的輸出候補。

   request
   {
       switch 1 {
           "かわいい"
           "天才"
           "サル"
           --
           "とは言い難い"
           --
           "ですね。"
           "かもしれません。"
       }
   }

request的輸出會變成"天才かもしれません。"。
中間的小組因為指定位置沒有候補,所以輸出空的字串,這點請注意一下。

迴圈(loop)

共有while、for、foreach這3種迴圈構造。

while

while是最簡單的迴圈形式,當條件為真時會重複執行{ }内的動作。

   request
   {
       _i = 1
       _j = 0
       while _i < 11 {
           _j += _i
           _i++
       }
       
       "將1到10全部加起來的話,可得到%(_j)。"
   }

上面的例子可以簡單說明while的功能。

下面的例子會產生10個不同的字串。request的輸出結果為報告1~10其中任一個數的平方根的字串。

   request
   {
       _i = 1
       while _i < 11 {
           "%(_i)的平方根為%(SQRT(_i))。"
           _i++
       }
   }

for

for是與while同様的先判定迴圈構造,其特色在於會在迴圈開始前指定其初期化式、脱出判定式、迴圈執行式。

while的例子用for來寫的話可寫成下面所示。

   request
   {
       for _i = 1; _i < 11; _i++ {
           "%(_i)的平方根為%(SQRT(_i))。"
       }
   }

_i = 1會在迴圈開始前執行。_i < 11為迴圈的條件式、如果條件為真時則迴圈繼續執行。_i++會在迴圈執行完一輪之後、開始新的一輪之前的時候執行。

C語言可用 for ( ; ; ) 作為無限迴圈,文的話各式的省略不可。
製作無限迴圈的時候,

   for 1;1;1

請這樣子寫。不過,用while的話

   while 1

這樣子寫就行了。因此文在製作無限迴圈的時候,不管是從可讀性或是動作速度的角度來看,都建議使用while迴圈。

foreach

照順序取出簡易陣列或泛用陣列的各元素値。

以下例子為取出簡易陣列的元素進行數值的轉換,再將這些數值全部加起來後輸出其結果。

   request
   {
       _str = "1,3,5,7,9"
       _t   = 0
       foreach _str; _i {
           _t += TOINT(_i)
       }
       _t
   }

foreach會持續記述處理對象。上面的例子中foreach指定簡易陣列為_str,然後依次將取出的陣列元素儲存在變數_i中。

即使處理對象的分隔符號被SETDELIM改變了也沒關係,foreach會依照此分隔符號正常的運作。

foreach可處理泛用陣列。

   request
   {
       _sent = ("I", "am", 31, "years", "old.")
       _t   = ""
       foreach _sent; _i {
           _t += (_i + " ")
       }
       _t
   }

request會輸出"I am 31 years old. "。

即使在foreach迴圈内將元素取出對象的簡易陣列、泛用陣列進行更改也沒關係。
變更會正常的反映出來。

break

迴圈中出現break的話,會跳出現下實行中最深的迴圈。

   request
   {
       _j = 0
       for _i = 0; _i < 100; _i++ {
           _j = _i*_i
           if  _j >= 100
               break
       }
       _i - 1
   }

上述的例子中、for為初期値0的_i實行至_i達到100才結束的迴圈。然而,由於迴圈内有被解釋為「_i 的平方值大於等於 100 時便跳脫迴圈」的break存在,因此實際上在 _i = 10 的時候迴圈便會結束了。

request會傳回比_i小1的值,也就是說整個函式request的實際意義為「求平方的結果不超過100的最大整數」。

continue

迴圈中出現continue的時候,會回到迴圈的開頭。

   request
   {
       _j = ""
       for _i = 0; _i < 3; _i++
       {
           _j += "go "
           if  _i > 0
               continue
           _j += "ahead "
       }
       _j
   }

_i在0、1、2之間變化,當_i等於1、2時continue會被執行,此時_j就不會被追加"ahead "字串。 因此request的輸出會變成"go ahead go go "。

return

return出現時,函式的運行便會在此結束。
函式的輸出會從到目前為止所產生的候選中挑選。

   to_rad
   {
       if GETTYPE(_argv[0]) == 3 {
           -1
           return
       }
       _argv[0]*2.0*3.14/360.0
   }

函式to_rad會將角度的單位從度轉換為弧(radian)。
若參數為字串時,if條件式會判定為真而傳回-1。由於return發生的時候僅有-1這個輸出候補,因此只會輸出此一值。

預處理(preprocess)

預處理為讀取辭書檔案階段時所執行的命令。

#define

在讀取辭書檔案之前(pass前的)產生的字串進行比對,並且直接將字串置換。

   #define before after

在讀入此記述後,若發現before這個字的話會自動置換成after。

#define 的有効範圍從宣告的下一行開始直到這個檔案的終端。

按記述的順序進行置換,寫在前頭的先變換。

#globaldefine

   #globaldefine before after

功能與 #define 命令相同,但是兩者的有効範圍不同。

#globaldefine 宣告後,下一行後的全部範圍(也包括之後讀取的辭書檔案)皆有効。也就是說,如果在最初讀取的辭書檔案前頭記述 #globaldefine 的話,其有効範圍即為所有的辭書檔案。

#define會先被處理。#globaldefine 在#define 置換後才被執行。

   #globaldefine tea green
   #define tea milk
   "teacup"

置換結果為"milkcup"。

預約語

以下的單字為系統函式名及控制命令名。
這些名字在文的系統中被保留了。使用者不能作成和這些名稱完全相符的函式或變數。

TOINT TOREAL TOSTR GETTYPE ISFUNC ISVAR  LOGGING GETLASTERROR 
LOADLIB UNLOADLIB REQUESTLIB CHARSETLIB RAND FLOOR  CEIL ROUND
SIN COS TAN LOG LOG10 POW SQRT STRSTR STRLEN REPLACE SUBSTR
ERASE INSERT TOUPPER TOLOWER CUTSPACE TOBINSTR TOHEXSTR BINSTRTOI
HEXSTRTOI CHR FOPEN FCLOSE FREAD FWRITE FWRITE2 FCOPY FMOVE 
MKDIR RMDIR  FDEL FRENAME FSIZE FENUM FCHARSET ARRAYSIZE 
SETDELIM EVAL ERASEVAR  GETTIME GETTICKCOUNT GETMEMINFO RE_SEARCH
RE_MATCH RE_GREP SETLASTERROR  RE_REPLACE RE_SPLIT RE_GETSTR 
RE_GETPOS RE_GETLEN CHRCODE ISINTSTR  ISREALSTR IARRAY SPLITPATH 
CVINT CVSTR CVREAL LETTONAME LSO STRFORM ANY  SAVEVAR 
GETSTRBYTES
   if elseif else case when others switch while for break continue return foreach

以下的單字/文字為運算子。
使用者無法作成、利用與這些名稱完全一致的變數或函式。還有,變數或函式名也不能含有這些單字/文字。

   ( ) [ ] ! ++ -- * / % + - & == != <= >= < > _in_ !_in_ && ||
   = := += -= *= /= %= +:= -:= *:= /:= %:= ,= 

謝辞

以下のライブラリを利用もしくは参考にさせていただきました。感謝致します。


*1 詳情參見 AYA人格範本

首頁   編輯 封鎖 差異 備份 上傳檔案 複製 變更名稱 重新載入   新建條目 一覽 搜索 最近的變更   幫助   最近更新的RSS
Last-modified: 2013-01-29 (二) 13:09:27 (3920d)